/*
 * Copyright (C) 2018-2020 Alibaba Group Holding Limited
 */

#include <aos/debug.h>
#include "lscreen_stream/stream.h"

#define TAG                    "lstream"

static struct {
    int                     cnt;
    const struct lstream_ops *ops[STREAM_OPS_MAX];
} g_streamers;

// static const struct lstream_ops* _get_stream_ops_by_type(uint8_t type)
// {
//     int i;

//     for (i = 0; i < g_streamers.cnt; i++) {
//         if (g_streamers.ops[i]->type == type) {
//             return g_streamers.ops[i];
//         }
//     }

//     LOGE(TAG, "get stream ops err, type = %d", type);
//     return NULL;
// }

static const struct lstream_ops* _get_stream_ops_by_url(const char *url)
{
    int i, j;

    for (i = 0; i < g_streamers.cnt; i++) {
        const char *const*protocols = g_streamers.ops[i]->protocols;

        for (j = 0; protocols[j]; j++) {
            int plen = strlen(protocols[j]);

            if (strncasecmp(url, protocols[j], plen) == 0) {
                /* 3 is '://'  */
                if (strlen(url) > (plen + 3)) {
                    if (strncasecmp(url + plen, "://", 3) == 0) {
                        return g_streamers.ops[i];
                    }
                } else {
                    goto err;
                }
            }
        }
    }

err:
    LOGE(TAG, "get stream ops err, url = %s", url);

    return NULL;
}

/**
 * @brief  regist stream ops
 * @param  [in] ops
 * @return 0/-1
 */
int lstream_ops_register(const struct lstream_ops *ops)
{
    int i;

    if (ops && (g_streamers.cnt < STREAM_OPS_MAX)) {
        for (i = 0; i < g_streamers.cnt; i++) {
            if (strcmp(ops->name, g_streamers.ops[i]->name) == 0) {
                /* replicate  */
                break;
            }
        }

        if (i == g_streamers.cnt) {
            g_streamers.ops[g_streamers.cnt] = ops;
            g_streamers.cnt++;
        }
        return 0;
    }

    LOGE(TAG, "stream ops regist fail\n");
    return -1;
}

void lstream_ops_register_all(void)
{
    extern struct lstream_ops lstream_ops_file;
    lstream_ops_register(&lstream_ops_file);

    extern struct lstream_ops lstream_ops_mem;
    lstream_ops_register(&lstream_ops_mem);
}

/**
* @brief  open a stream
* @param  [in] url
* @param  [in] stm_cnf
* @return NULL on error
*/
lstream_cls_t* lstream_open(const char *url)
{
    int ret;
    char *name      = NULL;
    lstream_cls_t *o = NULL;
    const struct lstream_ops *ops;

    CHECK_PARAM(url, NULL);
    name = strdup(url);
    CHECK_RET_TAG_WITH_GOTO(name, err);

    //FIXME:
    // ext = get_extension(name);
    // if (ext && stm_cnf->need_parse && (strncasecmp(ext, "m3u", 3) == 0))
    //     ops = _get_stream_ops_by_type(STREAM_TYPE_HLS);
    // else
    ops = _get_stream_ops_by_url(name);
    CHECK_RET_TAG_WITH_GOTO(ops, err);

    o = aos_zalloc(sizeof(lstream_cls_t));
    CHECK_RET_TAG_WITH_GOTO(o, err);
    o->url                   = name;
    o->ops                   = ops;

    aos_mutex_new(&o->lock);
    ret = ops->open(o);
    if (ret < 0) {
        LOGE(TAG, "open failed, url = %s", url);
        goto err;
    }

    return o;
err:
    aos_free(name);
    if (o) {
        aos_mutex_free(&o->lock);
        aos_free(o);
    }
    return NULL;
}

/**
 * @brief  read from a stream
 * @param  [in] o
 * @param  [in] buf
 * @param  [in] count
 * @return -1 when err happens
 */
int lstream_read(lstream_cls_t *o, uint8_t *buf, size_t count)
{
    int ret;
    //int len = count;

    CHECK_PARAM(o && buf && count, -1);
    CHECK_PARAM(!o->eof, 0);

    ret = o->ops->read ? o->ops->read(o, buf, count) : -1;
    if (ret < 0) {
        LOGE(TAG, "read failed");
    }

    return ret;
}

/**
 * @brief  write data to a stream
 * @param  [in] o
 * @param  [in] buf
 * @param  [in] count
 * @return -1 when err happens
 */
int lstream_write(lstream_cls_t *o, const uint8_t *buf, size_t count)
{
    int ret = -1;

    CHECK_PARAM(o, -1);

    aos_mutex_lock(&o->lock, AOS_WAIT_FOREVER);
    ret = o->ops->write ? o->ops->write(o, buf, count) : -1;
    if (ret < 0) {
        LOGE(TAG, "write failed");
    }
    aos_mutex_unlock(&o->lock);

    return ret;
}

/**
 * @brief  close a stream
 * @param  [in] o
 * @return 0/-1
 */
int lstream_close(lstream_cls_t *o)
{
    int ret = -1;
    //unsigned int flag;

    CHECK_PARAM(o, -1);
    aos_mutex_lock(&o->lock, AOS_WAIT_FOREVER);

    ret = o->ops->close(o);
    aos_mutex_unlock(&o->lock);

    aos_mutex_free(&o->lock);
    aos_free(o->url);
    aos_free(o);

    return ret;
}

/**
 * @brief  stream is eof whether
 * @param  [in] o
 * @return 0/1
 */
int lstream_is_eof(lstream_cls_t *o)
{
    CHECK_PARAM(o, -1);
    return o->eof;
}

/**
 * @brief  get the stream size
 * @param  [in] o
 * @return -1 on err
 */
int64_t lstream_get_size(lstream_cls_t *o)
{
    CHECK_PARAM(o, -1);
    return o->size;
}

/**
 * @brief  get the url
 * @param  [in] o
 * @return
 */
const char* lstream_get_url(lstream_cls_t *o)
{
    CHECK_PARAM(o, NULL);
    return o->url;
}





